<template>
  <ul class="ul-selectable" ref="selectableContainer">
    <a-row :gutter="[8, 8]">
      <a-col v-for="item in optionList" :key="item.name" :span="colSpan">
        <li class="li-selectable" :value="item.value">
          {{ item.name }}
        </li>
      </a-col>
    </a-row>
  </ul>
</template>

<script lang="ts">
import { defineComponent, markRaw } from 'vue'
import Selectable from 'selectable.js'

export default defineComponent({
  name: 'DragSelect',
  props: {
    modelValue: Array,
    optionList: {
      type: Array,
      default() {
        return []
      }
    },
    colSpan: {
      type: Number,
      default: 2,
    },
  },
  emits: [
    'update:modelValue',
    'change'
  ],
  data() {
    return {
      selectable: null,
    }
  },
  computed: {
  },
  watch: {
    modelValue(newValue, oldValue) {
      if (Array.isArray(oldValue) && oldValue.length > 0) {
        this.$refs.selectableContainer.querySelectorAll('li').forEach(eo => {
          if (oldValue.includes(eo._value)) {
            this.selectable.deselect(eo)
          }
        })
      }
      if (Array.isArray(newValue) && newValue.length > 0) {
        this.$refs.selectableContainer.querySelectorAll('li').forEach(eo => {
          if (newValue.includes(eo._value)) {
            this.selectable.select(eo)
          }
        })
      }
    },
  },
  methods: {
    init() {
      this.selectable = markRaw(new Selectable({
        container: this.$refs.selectableContainer,
        filter: ".li-selectable",
        toggle: true
      }))
      this.selectable.on("end", (e, selected, unselected) => {
        if (Array.isArray(selected) && selected.length > 0) {
          this.$emit('update:modelValue', [ ...new Set([ ...this.modelValue, ...selected.map(item => item.node._value) ]) ])
          this.$emit('change', [ ...new Set([ ...this.modelValue, ...selected.map(item => item.node._value) ]) ])
        }
        if (Array.isArray(unselected) && unselected.length > 0) {
          this.$emit('update:modelValue', [ ...this.modelValue.filter(eo => eo !== unselected[0].node._value) ])
          this.$emit('change', [ ...this.modelValue.filter(eo => eo !== unselected[0].node._value) ])
        }
      })
    },
    selectAll() {
      this.selectable.selectAll()
      this.$emit('update:modelValue', [ ...new Set([ ...this.optionList.map(item => item.value) ]) ])
      this.$emit('change', [ ...new Set([ ...this.optionList.map(item => item.value) ]) ])
    },
    clear() {
      this.selectable.clear()
      this.$emit('update:modelValue', [])
      this.$emit('change', [])
    },
    select(value) {
      if (Array.isArray(value) && value.length > 0) {
        this.$refs.selectableContainer.querySelectorAll('li').forEach(eo => {
          if (value.includes(eo._value)) {
            this.selectable.select(eo)
          }
        })
        this.$emit('update:modelValue', [ ...new Set([ ...this.modelValue, ...value.map(item => item) ]) ])
        this.$emit('change', [ ...new Set([ ...this.modelValue, ...value.map(item => item) ]) ])
      }
    },
  },
  mounted() {
    this.init()
    if (Array.isArray(this.modelValue) && this.modelValue.length > 0) {
      this.$refs.selectableContainer.querySelectorAll('li').forEach(eo => {
        if (this.modelValue.includes(eo._value)) {
          this.selectable.select(eo)
        }
      })
    }
  },
  beforeUnmount() {
    this.selectable.destroy()
  },
})
</script>

<style scoped lang="less">

.ul-selectable {
  border: 1px dashed #EEEEEE;
  padding: 8px;
}

.li-selectable {
  list-style: none;
  border: 1px dashed #EEEEEE;
  border-radius: 3px;
  word-wrap: break-word;
  text-align: center;
}

.li-selectable:hover {
  border-color: @primary-3;
}

.li-selectable.ui-selecting {
  background: @primary-1;
}

.li-selectable.ui-selected {
  border: 1px dashed @primary-3;
  background: @primary-1;
}

</style>
